require'zsvc.evhandle'
require'zsvc.accept'
pcall(require'zvty')
module(..., package.seeall)

local Telnet= {}
zsvc.evhandle.InitEvent(Telnet)
local Telnet_mt = {__metatable = {}, __index = Telnet}

function MakeInhMT(ch)
	ch=ch or {}
	for _i,_j in pairs(Telnet) do
		ch[_i]=_j
	end
	return ch
end

function new(params,childmt)
	local obj = setmetatable({logger=params.logger,react=params.react,fd=params.fd},childmt or Telnet_mt)
	obj.writebuffer=''
	obj.recvbuffer=''
	
	obj:_bv_init()
	return obj
end

function Telnet:start_tty()
	if zvty then
		local obj=self
		obj.vty=zvty.new{
			outbuf_reset=function(...) return obj:vty_outbuf_reset(...) end,
			auth=function(...) return obj:vty_auth(...) end,
			write=function(...) return obj:vty_write(...) end,
			prompt=function(...) return obj:vty_prompt(...) end,
			execute=function(...) return obj:vty_execute(...) end,
			complete_command=function(...) return obj:vty_complete_command(...) end,
			hello=function(...) return obj:vty_hello(...) end,
		}
	end
end
function Telnet:vty_outbuf_reset()
	self.writebuffer=''
end
function Telnet:vty_auth(name,pwd)
	return zvty.XXX_NODE,"auth ok\n"
end
function Telnet:vty_write(str)
	if not str then return end
	str=string.gsub(str,"\n",'\r\n')
	self:Write(str)
end
function Telnet:vty_prompt()
	self:Write(">")
end
function Telnet:vty_execute()
	if not self:OnCliCmd(self.fd,self.vty:getbuf()) then
		if self.vty then 
			self.vty:setstat(zvty.CLOSE)
		end
	end
end

function Telnet:vty_complete_command()
end

function Telnet:vty_hello()
	self:Write("Hello\n")
end

function Telnet:vty_break()
	
end

function Telnet:_bv_cb_read(bv)
end
function Telnet:_bv_cb_write(bv)
end
function Telnet:OnClose()
	self:vty_break()
	self.logger:info('Telnet close fd for ' .. (self.fd:getpeername() or ''))
	if self.fd then
		self:_bv_close()
		self.fd:close()
	end
	self.fd=nil
end

function Telnet:OnCliCmd(fd,line)
	self.logger:info('OnCliCmd recv cmd:'.. tostring(line)) 
	self:Write(tostring(line))
	return true
end

function Telnet:Write(line)
	self.writebuffer=self.writebuffer .. line
	if not self.__ev_send then
		self.__ev_send=self.react:AddEventCB(self.OnClose,self,self:GetFD(),self.react.EV_WRITE,self.SendCB,self.wr__timeout)
	end
	return
end

function Telnet:SendCB(event)
	local fd=self:GetFD()
	if #self.writebuffer == 0 then
		self.__ev_send=nil
		return -1
	end
	local npos,err,pos=self.react.sendfd(fd,self.writebuffer,pos)
	if not npos and err ~= "timeout" and err ~= "wantwrite"then
		self.logger:info((self.name or '') .. ' socket error ' .. tostring(err))
		self:OnClose()
		return -1
	end
	self.writebuffer = string.sub( self.writebuffer, npos + 1)
	return self.react.EV_WRITE,self.wr__timeout
end

function Telnet:RecvCB(event)
	local fd=self:GetFD()
	local r,err,_r=self.react.recvfd(fd,512)
	if not r and err ~= "timeout" and err ~= "wantread" then
		self.logger:info((self.name or '') .. ' socket error ' .. tostring(err))
		self:OnClose()
		return -1
	end
	r= r or _r
	if self.vty then
		if not self.vty:onread(r) then
			self:OnClose()
			return -1
		end
		return self.react.EV_READ,self.rd__timeout
	else
		self.recvbuffer=self.recvbuffer .. r
		repeat
			local npos=string.find(self.recvbuffer,'\n',1,1)
			if npos then
				local onecmd=string.sub(self.recvbuffer,1,npos)
				self.recvbuffer=string.sub(self.recvbuffer,npos+1)
				local r,err=self:OnCliCmd(fd,onecmd)
				if not r then
					self.logger:info((self.name or '') .. ' cmd msg ' .. tostring(err))
					self:OnClose()
					return -1
				end
			else
				return self.react.EV_READ,self.rd__timeout
			end
		until (false)
	end
end
